python面向对象(二)

面向对象三大特性在python中的体现

封装

  • 封装的理解

    封装是属性和方法的抽象。让数据和代码成为类的过程。

    对属性和方法进行定义、隔离、保护。

    形成一个类对外可操作属性和方法的接口

    隐藏实现细节,使得代码模块化

  • 私有属性VS公开属性、私有方法VS公开方法

    由于属性和方法都可以看作是类内部的对象,所以下面简称二者为’对象‘。

    公开对象可以在类内部和外部使用

    私有对象只能在类内部使用,外部直接调用时会发生AttributeError

    如需定义一个私有属性,只需要在属性名中加双下划线前缀__。这种处理方法只是修改了原有属性的名字,如果非要访问私有属性和私有方法也可以,在原名字前加下划线和类名。如_Person__count

    私有化从形式上保护了python类内部使用的逻辑,是一种程序员间的约定

  • 保留属性

    也叫特殊属性,双下划线开头和结尾

    为理解python类提供了统一的属性接口

    属性值具有特定含义,类定义后直接使用

    | 类对象的保留属性 | 描述 |
    | —————- | ————————————— |
    | __name__ | 类的名字 |
    | __qualname__ | 以.分隔的从模块全局命名空间开始的类名称 |
    | __bases__ | 类所继承的基类名称 |
    | __dict__ | 类成员信息的字典 |

    | 实例对象的保留属性 | 描述 |
    | —————— | —————————————— |
    | __dict__ | 对象属性信息的字典,key是属性,value是值 |
    | __class__ | 类对象对象的类信息,即type信息 |
    | __doc__ | 类描述,写在类定义下的首行字符串,不能继承 |
    | __module__ | 类所在模块的名字 |

  • 保留方法

    也叫特殊方法,以双下划线开头和结尾的方法。

    为操作python类提供了统一的方法接口

    | 常用保留方法 | 对应操作 | 描述 |
    | ————— | —————– | ——————————– |
    | __init__() | obj = ClassName() | 初始化实例对象 |
    | __del__() | del obj | 删除实例对象 |
    | __repr__() | repr(obj) | 定义对象可打印字符串的函数逻辑 |
    | __str__() | str(obj) | 定义对象字符串转换操作的函数逻辑 |
    | __bytes__() | bytes(obj) | 字节串转换 |
    | __format__() | obj.format() | 格式化输出 |
    | __hash__() | hash(obj) | 哈希操作 |
    | __bool__() | bool(obj) | 布尔运算 |
    | __len__() | len(obj) | 对象长度 |
    | __reversed__() | obj.reversed() | 对象逆序 |
    | __abs__() | abs(obj) | 求绝对值 |
    | __int__() | int(obj) | 整数转换 |
    | __lt__() | obj1 < obj2 | 比较操作 |
    | __le__() | obj1 <= obj2 | 比较操作 |
    | __eq__() | obj1 == obj2 | 比较操作 |
    | __ne__() | obj1 != obj2 | 比较操作 |
    | __gt__() | obj1 > obj2 | 比较操作 |
    | __ge__() | obj1 >= obj2 | 比较操作 |
    | 等等 | | |

继承 Inheritance

  • 什么是继承

    原有的、已经写好的类,被继承的类叫做基类,也叫父类

    新构建的、继承父类的类叫做派生类,也叫子类

    父类的父类叫做超类

    一个子类可以有多个父类,这种继承方式称为多继承

    扩展已存在的代码模块(类),实现了以类为单位的高抽象级别代码复用。

  • 如何使用【[多]继承】特性

    1
    2
    3
    4
    5
    6
    # 在定义类时声明继承关系
    # 父类名可以带有路径:ModuleName.BaseClassName
    # 父类可以有多个,即多继承
    class 类名(父类名1,父类名2,...,父类名n):
    def __init__(self, 参数)
    语句块

    子类基本可以完全拥有父类的属性和方法,并且根据需要可以重写继承来的属性和方法,也可以新增属性和方法。

    所谓“基本完全拥有”指的是:子类不能继承父类的私有属性和私有方法

    使用父类的类方法和类属性时,要使用父类的类名调用。或者super()

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    class people():
    count = 0

    def __init__(self, name):
    self.name = name
    people.count += 1

    def get_name(self):
    return self.name


    class man(people):
    def __init__(self, name, wife):
    people.__init__(self, name)
    # super(man, self).__init__(name) # 也可以
    self.wife = wife

    def marrywho(self):
    return self.wife


    p0 = people('小王')
    p1 = man('小张', '小李')
    print(p1.get_name())
    print(p1.marrywho())
    print(people.count)
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    # 多继承的规则:深度优先,从左至右
    # 所有属性、方法,以及super()也遵从这一原则
    # 所以,多继承中父类的顺序很关键
    class Cuboid(object):
    def __init__(self, length, width, heigth):
    self.length = length
    self.width = width
    self.heigth = heigth

    @property
    def volume(self):
    return self.heigth * self.width * self.length

    def say(self):
    print('本长方体的规格是%f*%f*%f' % (self.length, self.width, self.heigth))


    class Iron(object):
    def __init__(self, density):
    self.density = density

    def say(self):
    print('铁的密度是%f' % self.density)


    class Slab(Cuboid, Iron):
    def __init__(self, length, width, heigth, density):
    Cuboid.__init__(self, length, width, heigth)
    Iron.__init__(self, density)

    @property
    def mass(self):
    return self.volume * self.density


    if __name__ == '__main__':
    block = Slab(2, 0.5, 0.5, 7.9e3)
    print(block.mass)
    block.say()
  • 判定继承关系的2个内置函数

    | 函数 | 描述 |
    | ———————- | —————————————————— |
    | isinstance(obj, cls) | 判断对象obj是否是类cls的实例或子类实例,返回True/False |
    | issubclass(cls1, cls2) | 判断类cls1是否是类cls的子类,返回True/False |

  • python最基础类:object

    所有类定义时默认继承object类

    保留属性和保留方法本质上是object类的属性和方法。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    print(object.__name__)
    print(object.__doc__)
    print(object.__bases__)
    print(object.__class__)
    print(object.__module__)
    '''
    object
    The most base type
    ()
    <class 'type'>
    builtins
    '''
  • 类的方法覆盖和属性覆盖

    覆盖原则:就近覆盖

    优先使用子类重写的属性和方法

    如果子类没有重写,则去寻找父类的属性和方法

    如果父类中也没有,则去寻找超类的属性和方法

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    class A(object):
    var = 999

    def say(self):
    print('AAA')

    # 未重写/覆盖A的类属性var和实例方法say
    class B(A):
    pass


    # 重写A的类属性var和实例方法say
    class C(A):
    var = 0

    def say(self):
    super().say()
    print('I am C')


    print(B.var)
    print(C.var)

    a = A()
    a.say()

    b = B()
    b.say()

    c = C()
    c.say()
    '''
    999
    0
    AAA
    AAA
    I am C
    AAA
    '''

多态

  • 多态的理解

    同一操作作用于不同的对象,可以有不同的解释,产生不同的结果,这就是多态。

    一种接口多种实现。

    封装和继承实现的是代码重用,多态实现的是接口重用。

    多态是父类使用子类的方法。

    继承是子类使用父类的方法。

    在C++中是通过派生类覆写基类中的虚函数型方法来实现的。简单来说就是将子类类型的指针赋值给父类类型的指针。编译时多态性通过重载实现;运行时多态性通过覆写虚成员实现。重载是对同名函数的不同参数列表实现。覆写是同名函数、同参数列表、同返回值类型的实现。

    由于python是动态语言以及强大的参数传递功能,重载这一特性天然支持。当然,重载并不等同于多态。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    class Animal(object):
    def run(self):
    print('Animal is running...')


    class Dog(Animal):

    def run(self):
    print('Dog is running...')


    class Cat(Animal):

    def run(self):
    print('Cat is running...')


    def run_twice(animal):
    animal.run()
    animal.run()


    if __name__ == '__main__':
    d = Dog()
    c = Cat()
    print(isinstance(d, Animal))
    run_twice(d)
    run_twice(c)

    对扩展开放,对修改封闭。

  • 参数类型的多态

  • 参数形式的多态